---
title: Arbitrary updates
description: Non-standard query management from custom components
hide_table_of_contents: true
---

import { SandpackRQB } from '@site/src/components/SandpackRQB';

Sometimes standard component actions don't meet specific requirements. React Query Builder provides tools to extend or replace the default behavior with custom implementations.

## Multiple action elements

The default `addRuleAction` component always creates rules using the default configuration. However, you might need to provide multiple buttons that add rules with different pre-selected fields based on which button is clicked.

This example demonstrates a custom component that renders two buttons in place of the standard `addRuleAction`. Each button corresponds to a specific field. The click handler uses `props.schema.getQuery()` to retrieve the current query and `props.schema.dispatchQuery()` to update the query with the appropriate field selection.

<SandpackRQB rqbVersion={7} options={{ editorHeight: 480 }}>

```tsx
import { useState } from 'react';
import {
  ActionElement,
  ActionProps,
  add,
  Field,
  QueryBuilder,
  RuleGroupType,
} from 'react-querybuilder';
import 'react-querybuilder/dist/query-builder.css';

const AddRulesAction = (props: ActionProps) => {
  const onClick = (field: string) => {
    props.schema.dispatchQuery(
      add(props.schema.getQuery(), { field, operator: '=', value: '' }, props.path)
    );
  };
  return (
    <>
      <ActionElement
        {...props}
        label="+ First Name Rule"
        handleOnClick={() => onClick('firstName')}
      />
      <ActionElement
        {...props}
        label="+ Last Name Rule"
        handleOnClick={() => onClick('lastName')}
      />
    </>
  );
};

const fields: Field[] = [
  { name: 'firstName', label: 'First Name' },
  { name: 'lastName', label: 'Last Name' },
];

const initialQuery: RuleGroupType = {
  combinator: 'and',
  rules: [],
};

export default () => {
  const [query, setQuery] = useState(initialQuery);

  return (
    <div>
      <QueryBuilder
        fields={fields}
        query={query}
        onQueryChange={setQuery}
        controlElements={{ addRuleAction: AddRulesAction }}
      />
    </div>
  );
};
```

</SandpackRQB>

## Custom query properties

While the standard query structure works well, it may not contain all the information you need. You might require additional properties in the query hierarchy and want to manage these properties through custom controls within the query builder. This example adds a `caseSensitive` property to each rule, using a checkbox alongside the default value editor to toggle this setting.

This example also demonstrates how the `caseSensitive` property integrates with a custom [rule processor](../utils/export#rule-processor) for `formatQuery`. When `caseSensitive` is false, the processor wraps the field with SQL's `LOWER` function and converts the value to lowercase before passing it to the default rule processor.

The example displays both the generated SQL query and the raw query object below the query builder interface.

> _Related: [Adding and removing query properties](./adding-removing-query-properties)_

<SandpackRQB rqbVersion={7} options={{ editorHeight: 480 }}>

```tsx
import { useState } from 'react';
import {
  defaultRuleProcessorSQL,
  Field,
  formatQuery,
  QueryBuilder,
  RuleGroupType,
  RuleProcessor,
  update,
  ValueEditor,
  ValueEditorProps,
} from 'react-querybuilder';
import 'react-querybuilder/dist/query-builder.css';

const CaseSensitivityValueEditor = (props: ValueEditorProps) => {
  const onChange = () => {
    props.schema.dispatchQuery(
      update(props.schema.getQuery(), 'caseSensitive', !props.rule.caseSensitive, props.path)
    );
  };
  return (
    <>
      <ValueEditor {...props} />
      <label>
        <input type="checkbox" checked={!!props.rule.caseSensitive} onChange={onChange} />
        {' Case sensitive'}
      </label>
    </>
  );
};

const ruleProcessor: RuleProcessor = (rule, opts) => {
  if (!rule.caseSensitive) {
    return defaultRuleProcessorSQL(
      { ...rule, field: `LOWER(${rule.field})`, value: rule.value.toLocaleLowerCase() },
      opts
    );
  }
  return defaultRuleProcessorSQL(rule, opts);
};

const fields: Field[] = [
  { name: 'firstName', label: 'First Name' },
  { name: 'lastName', label: 'Last Name' },
];

const initialQuery: RuleGroupType = {
  combinator: 'or',
  rules: [
    { field: 'firstName', operator: '=', value: 'Adam', caseSensitive: true },
    { field: 'firstName', operator: '=', value: 'Eve', caseSensitive: false },
  ],
};

export default () => {
  const [query, setQuery] = useState(initialQuery);

  return (
    <div>
      <QueryBuilder
        fields={fields}
        query={query}
        onQueryChange={setQuery}
        controlElements={{ valueEditor: CaseSensitivityValueEditor }}
      />
      <pre>
        <code>{formatQuery(query, { format: 'sql', ruleProcessor })}</code>
      </pre>
      <pre>
        <code>{JSON.stringify(query, null, 2)}</code>
      </pre>
    </div>
  );
};
```

</SandpackRQB>
